package poker;

import java.util.Arrays;

public class HandPowerRating 
{	
	/**
	 * Returns the highest rating of your current hand
	 * @param hand
	 * @return
	 */
	public static int[] rate(Card[] hand)
	{
		if(isStraightFlush(hand))
			return calculateStraightFlushPower(hand);
		if(isFourOfAKind(hand))
			return calculateFourOfAKindPower(hand);
		if(isFullHouse(hand))
			return calculateFullHousePower(hand);
		if(isFlush(hand))
			return calculateFlushPower(hand);
		if(isStraight(hand))
			return calculateStraightPower(hand);
		if(isThreeOfAKind(hand))
			return calculateThreeOfAKindPower(hand);
		if(isTwoPair(hand))
			return calculateTwoPairPower(hand);
		if(isOnePair(hand))
			return calculateOnePairPower(hand);

		return calculateHighCardPower(hand);
	}

	public static int compare(Card[] hand1, Card[] hand2)
	{		
		int[] rating1 = rate(hand1);
		int[] rating2 = rate(hand2);
		
		if(rating1[0] > rating2[0])
			return 1;
		
		else if(rating1[0] < rating2[0])
			return -1;
		else
		{
			for (int i = 1; i < rating1.length; i++) 
			{
				if(rating1[i] > rating2[i])
					return 1;
				else if(rating1[i] < rating2[i])
					return -1;
			}
			return 0;
		}
	}

	private static boolean isStraightFlush(Card[] hand)
	{
		int flush = getFlush(hand);
		if(flush == -1)
			return false;
		char suit = (char)flush;
		Card[] flushCards = CardUtil.getBySuit(hand, suit);
		Arrays.sort(flushCards);
		return isStraight(flushCards);
	}

	private static boolean isFourOfAKind(Card[] hand)
	{
		if(hand.length < 4)
			return false;

		Card[] handCopy = (Card[])hand.clone();
		Arrays.sort(handCopy);

		return hasSameKind(handCopy, 4) > 0 ? true : false;
	}

	private static boolean isFullHouse(Card[] hand)
	{
		if(hand.length < 5)
			return false;

		Card[] handCopy = (Card[])hand.clone();
		Arrays.sort(handCopy);
		int threeOfAKind = hasSameKind(handCopy, 3);

		handCopy = CardUtil.removeByValue(handCopy, threeOfAKind);
		int pair = hasSameKind(handCopy, 2);

		if(threeOfAKind != -1 && pair != -1)
			return true;
		return false;
	}

	private static boolean isFlush(Card[] hand)
	{ 
		return (getFlush(hand) != -1);
	}	

	private static boolean isStraight(Card[] hand)
	{
		if(hand.length < 5)
			return false;

		Card[] handsCopy = (Card[])hand.clone();
		Arrays.sort(handsCopy);

		int startPosition = isStraightCheck(handsCopy);

		return (startPosition != -1 ? true : false);
	}

	private static boolean isThreeOfAKind(Card[] hand)
	{
		if(hand.length < 3)
			return false;

		Card[] handCopy = (Card[])hand.clone();
		Arrays.sort(handCopy);

		return hasSameKind(handCopy, 3) > 0 ? true : false;
	}

	private static boolean isTwoPair(Card[] hand)
	{
		if(hand.length < 4)
			return false;

		Card[] handCopy = (Card[])hand.clone();
		Arrays.sort(handCopy);

		int firstPair = hasSameKind(handCopy, 2);
		handCopy = CardUtil.removeByValue(handCopy, firstPair);
		int secondPair = hasSameKind(handCopy, 2);

		if(firstPair!= -1 && secondPair != -1)
			return true;
		return false;
	}

	private static boolean isOnePair(Card[] hand)
	{
		if(hand.length < 2)
			return false;

		Card[] handCopy = (Card[])hand.clone();
		Arrays.sort(handCopy);

		return hasSameKind(handCopy, 2) > 0 ? true : false;
	}

	private static int[] calculateStraightFlushPower(Card[] hand)
	{
		Card[] handCopy = (Card[])hand.clone();
		Arrays.sort(handCopy);
		int index = isStraightCheck(handCopy);
		return new int[] { 9, handCopy[index].getValue() };
	}

	private static int[] calculateFourOfAKindPower(Card[] hand)
	{
		Card[] cardsCopy = (Card[])hand.clone();
		Arrays.sort(cardsCopy);
		int fourOfAKind = hasSameKind(cardsCopy, 4);
		cardsCopy = CardUtil.removeByValue(cardsCopy, fourOfAKind);

		return new int[] {8, fourOfAKind, cardsCopy[0].getValue() };
	}

	private static int[] calculateFullHousePower(Card[] hand)
	{
		Card[] cardsCopy = (Card[])hand.clone();
		Arrays.sort(cardsCopy);
		int threeOfAKind = hasSameKind(cardsCopy, 3);
		cardsCopy = CardUtil.removeByValue(cardsCopy, threeOfAKind);
		int pair = hasSameKind(cardsCopy, 2);

		return new int[] {7, threeOfAKind, pair};
	}

	private static int[] calculateFlushPower(Card[] hand)
	{	
		Card[] cardsCopy = (Card[])hand.clone();
		cardsCopy = CardUtil.getBySuit(cardsCopy, (char)getFlush(cardsCopy));
		Arrays.sort(cardsCopy);	
		
		return new int[] {6,  cardsCopy[0].getValue(), cardsCopy[1].getValue(), cardsCopy[2].getValue() , cardsCopy[3].getValue(), cardsCopy[4].getValue()};
	}

	private static int[] calculateStraightPower(Card[] hand)
	{
		Card[] cardsCopy = (Card[])hand.clone();
		Arrays.sort(cardsCopy);
		int index = isStraightCheck(cardsCopy);
		return new int[] {5, cardsCopy[index].getValue() };
	}
	private static int[] calculateThreeOfAKindPower(Card[] hand)
	{
		Card[] cardsCopy = (Card[])hand.clone();
		Arrays.sort(cardsCopy);
		int threeOfAKindValue = hasSameKind(cardsCopy, 3);
		Card[] temp = CardUtil.removeByValue(cardsCopy, threeOfAKindValue);

		return new int[] {4, threeOfAKindValue, temp[0].getValue(), temp[1].getValue()};
	}

	private static int[] calculateTwoPairPower(Card[] hand)
	{	
		Card[] cardsCopy = (Card[])hand.clone();
		Arrays.sort(cardsCopy);
		int firstPair = hasSameKind(cardsCopy, 2);
		cardsCopy = CardUtil.removeByValue(cardsCopy, firstPair);
		int secondPair = hasSameKind(cardsCopy, 2);
		cardsCopy = CardUtil.removeByValue(cardsCopy, secondPair);

		return new int[] {3, firstPair, secondPair, cardsCopy[0].getValue() };
	}
	private static int[] calculateOnePairPower(Card[] hand)
	{
		Card[] cardsCopy = (Card[])hand.clone();
		Arrays.sort(cardsCopy);
		int pair = hasSameKind(cardsCopy, 2);
		cardsCopy = CardUtil.removeByValue(cardsCopy, pair);
		return new int[] {2, pair, cardsCopy[0].getValue(), cardsCopy[1].getValue(), cardsCopy[2].getValue() };
	}
	private static int[] calculateHighCardPower(Card[] hand)
	{
		Card[] cardsCopy = (Card[])hand.clone();
		Arrays.sort(cardsCopy);
		return new int[] {1, cardsCopy[0].getValue(), cardsCopy[1].getValue(), cardsCopy[2].getValue() , cardsCopy[3].getValue(), cardsCopy[4].getValue()};
	}

	/**
	 * Checks if the hand consists of a straight
	 * @param cards
	 * @return the position of the first consecutive card, -1 if the number of consecutive cards doesn't exist.
	 */
	private static int isStraightCheck(Card[] hand)
	{	
		int number = 5;
		
		hand = CardUtil.removeDuplicates(hand);
		//Special CASE: check if ACE takes the value of 1
		if(hand.length < 5)
			return -1;
		
		if(hand[0].getValue() == 14)
		{
			int startPos = -1;
			if(hand[1].getValue() == 5)
				startPos=1;
			if(hand[2].getValue() == 5)
				startPos=2;
			if(hand[3].getValue() == 5)
				startPos = 3;
				
			if(startPos != -1)
			{
				int start = 5;
				int count = 0;
				for (int i = startPos; i < hand.length; i++) {
					if(hand[i].getValue() == start - (i - startPos))
						count++;
				}
				if(count == 4)
					return startPos;
			}	
		}

		for (int i = 0; i < hand.length; i++) {
			//there are not enough cards left
			if(number > hand.length - i)
				return -1;

			Card currentCard = hand[i];
			int consecutive = 1;
			for (int j = i + 1; j < i+number; j++) {
				int value = hand[j].getValue();
				//if the next card doesn't have the right value, break the loop
				if(!(currentCard.getValue() - (j - i) == value))
					break;
				consecutive++;
				//the number of executive cards is found
				if(consecutive == number)
					return i;
			}
		}
		return -1;
	}
	

	/**
	 * Checks if the hand consists of a given number of cards with equal value 
	 * @param hand A hand sorted by value
	 * @param number
	 * @return 
	 */
	private static int hasSameKind(Card[] hand, int number)
	{
		for (int i = 0; i < hand.length; i++) {
			//there are not enough cards left
			if(number > hand.length - i)
				return -1;

			int currentValue = hand[i].getValue();
			for (int j = i + 1; j < i+number; j++) {
				if(!(currentValue == hand[j].getValue()))
					break;
				//the number of executive cards is found
				if(j == i+number - 1)
					return currentValue;
			}
		}
		return -1;
	}

	
	public static String nameOfHand(int[] handPowerRating)
	{
		String[] nameOfHand = {"High Card", "One Pair", "Two Pair", "3 Of A Kind", "Straight", "Flush",
				"Full House", "4 Of A Kind", "Royal Flush"};
		String noHand = "Nothing";
		
		for (int i = 1; i < nameOfHand.length; i++) {
			if(handPowerRating[0] == i)	return nameOfHand[i-1];				
		}		
		return noHand;
	}
	
	
	/**
	 * Gives you the suit of the flush in your hand
	 * @param hand
	 * @return A suit or -1 if it's not a flush
	 */
	private static int getFlush(Card[] hand)
	{
		int spadesCount = 0;
		int heartCount = 0;
		int clubsCount = 0;
		int diamondsCount = 0;

		if(hand.length >= 5)
		{
			for (int i = 0; i < hand.length; i++) 
			{
				if(hand[i].getSuit() == 's') {spadesCount++;}
				if(hand[i].getSuit() == 'h') {heartCount++;}
				if(hand[i].getSuit() == 'c') {clubsCount++;}
				if(hand[i].getSuit() == 'd') {diamondsCount++;}
			}

			if(clubsCount >= 5)
				return 'c';
			if(heartCount >= 5)
				return 'h';
			if(spadesCount>= 5)
				return 's';
			if(diamondsCount >= 5)
				return 'd';
		}

		return -1;

	}
}